﻿#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdarg.h>
#include <unistd.h>
#include <sys/time.h>
#include <time.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include "ngx_c_conf.h"
#include "ngx_macro.h"
#include "ngx_global.h"
#include "ngx_func.h"
#include "ngx_c_socket.h"
#include "ngx_c_memory.h"
#include "ngx_c_lockmutex.h"
/***************************************************************
 * @file       ngx_c_socket_time.cxx
 * @brief      设置踢出时钟,用户三次握手成功连入且Sock_WaitTimeEnable = 1，
 *             那么本函数被调用
 * @author     txj
 * @version    v1
 * @return     
 * @date       2021/8/22
 **************************************************************/
void CSocekt::AddToTimerQueue(lpngx_connection_t pConn)
{
    CMemory *p_memory = CMemory::GetInstance();
    /*获取当前时间*/
    time_t futtime = time(NULL);
    /*设置定时时间，20秒之后的时间*/
    futtime += m_iWaitTime;

    CLock lock(&m_timequeueMutex); 
    LPSTRUC_MSG_HEADER tmpMsgHeader = (LPSTRUC_MSG_HEADER)p_memory->AllocMemory(m_iLenMsgHeader,false);
    tmpMsgHeader->pConn = pConn;
    tmpMsgHeader->iCurrsequence = pConn->iCurrsequence;
    /*按键(时间)自动排序 小->大*/
    m_timerQueuemap.insert(std::make_pair(futtime,tmpMsgHeader));
    /*计时队列尺寸+1*/
    m_cur_size_++;
    /*计时队列头部时间值保存到m_timer_value_里*/
    m_timer_value_ = GetEarliestTime(); 
    return;
}
/***************************************************************
 * @file       ngx_c_socket_time.cxx
 * @brief      从multimap中取得最早的时间返回去，调用者负责互斥
 *             所以本函数不用互斥，调用者确保m_timeQueuemap中一定不为空
 * @author     txj
 * @version    v1
 * @return     
 * @date       2021/8/22
 **************************************************************/
time_t CSocekt::GetEarliestTime()
{
    std::multimap<time_t, LPSTRUC_MSG_HEADER>::iterator pos;
    pos = m_timerQueuemap.begin();
    return pos->first;  
}
/***************************************************************
 * @file       ngx_c_socket_time.cxx
 * @brief      从m_timeQueuemap移除最早的时间，并把最早这个时间所在的
 *             项的值所对应的指针返回，调用者负责互斥，所以本函数不用互斥，
 * @author     txj
 * @version    v1
 * @return     
 * @date       2021/8/22
 **************************************************************/
LPSTRUC_MSG_HEADER CSocekt::RemoveFirstTimer()
{
    std::multimap<time_t, LPSTRUC_MSG_HEADER>::iterator pos;
    LPSTRUC_MSG_HEADER p_tmp;
    if(m_cur_size_ <= 0)
    {
        return NULL;
    }
    pos = m_timerQueuemap.begin();
    p_tmp = pos->second;
    m_timerQueuemap.erase(pos);
    --m_cur_size_;
    return p_tmp;
}
/***************************************************************
 * @file       ngx_c_socket_time.cxx
 * @brief      根据给的当前时间，从m_timeQueuemap找到比这个时间更老（更早）
 *             的节点1个返回去，这些节点都是时间超过了，要处理的节点
 *             调用者负责互斥，所以本函数不用互斥
 * @author     txj
 * @version    v1
 * @return     
 * @date       2021/7/29
 **************************************************************/
LPSTRUC_MSG_HEADER CSocekt::GetOverTimeTimer(time_t cur_time)
{   
    CMemory *p_memory = CMemory::GetInstance();
    LPSTRUC_MSG_HEADER ptmp;

    if (m_cur_size_ == 0 || m_timerQueuemap.empty())
        return NULL; //队列为空

    time_t earliesttime = GetEarliestTime(); //到multimap中去查询
    if (earliesttime <= cur_time)
    {
        //这回确实是有到时间的了超时的节点
        ptmp = RemoveFirstTimer();    //把这个超时的节点从 m_timerQueuemap 删掉，并把这个节点的第二项返回来；
        if(/*m_ifkickTimeCount == 1 && */m_ifTimeOutKick != 1)  //能调用到本函数第一个条件肯定成立，所以第一个条件加不加无所谓，主要是第二个条件
        {
            //如果不是要求超时就提出，则才做这里的事：
            //因为下次超时的时间我们也依然要判断，所以还要把这个节点加回来
            time_t newinqueutime = cur_time+(m_iWaitTime);
            LPSTRUC_MSG_HEADER tmpMsgHeader = (LPSTRUC_MSG_HEADER)p_memory->AllocMemory(sizeof(STRUC_MSG_HEADER),false);
            tmpMsgHeader->pConn = ptmp->pConn;
            tmpMsgHeader->iCurrsequence = ptmp->iCurrsequence;
            m_timerQueuemap.insert(std::make_pair(newinqueutime,tmpMsgHeader));
            m_cur_size_++;
        }
        if(m_cur_size_ > 0)
        {
            m_timer_value_ = GetEarliestTime(); 
        }
        return ptmp;
    }
    return NULL;
}
/***************************************************************
 * @file       ngx_c_socket_time.cxx
 * @brief      把指定用户tcp连接从timer表中抠出去
 * @author     txj
 * @version    v1
 * @return     
 * @date       2021/7/29
 **************************************************************/
void CSocekt::DeleteFromTimerQueue(lpngx_connection_t pConn)
{
    std::multimap<time_t, LPSTRUC_MSG_HEADER>::iterator pos,posend;
    CMemory *p_memory = CMemory::GetInstance();
    CLock lock(&m_timequeueMutex);
    //所以如下我们遍历整个队列找 一圈，而不是找到一次就拉倒，以免出现什么遗漏
lblMTQM:
    pos    = m_timerQueuemap.begin();
    posend = m_timerQueuemap.end();
    for(; pos != posend; ++pos) 
    {
        if(pos->second->pConn == pConn)
        {
            p_memory->FreeMemory(pos->second);//释放内存
            m_timerQueuemap.erase(pos);
            --m_cur_size_; //减去一个元素，必然要把尺寸减少1个;
            goto lblMTQM;
        }
    }
    if(m_cur_size_ > 0)
    {
        m_timer_value_ = GetEarliestTime();
    }
    return;
}
/***************************************************************
 * @file       ngx_c_socket_time.cxx
 * @brief      清理时间队列中所有内容
 * @author     txj
 * @version    v1
 * @return     
 * @date       2021/7/29
 **************************************************************/
void CSocekt::clearAllFromTimerQueue()
{   
    std::multimap<time_t, LPSTRUC_MSG_HEADER>::iterator pos,posend;

    CMemory *p_memory = CMemory::GetInstance(); 
    pos    = m_timerQueuemap.begin();
    posend = m_timerQueuemap.end();
    for(; pos != posend; ++pos) 
    {
        p_memory->FreeMemory(pos->second);
        --m_cur_size_;
    }
    m_timerQueuemap.clear();
}
/***************************************************************
 * @file       ngx_c_socket_time.cxx
 * @brief      时间队列监视和处理线程，处理到期不发心跳包的用户踢出的线程
 * @author     txj
 * @version    v1
 * @return     
 * @date       2021/7/29
 **************************************************************/
void* CSocekt::ServerTimerQueueMonitorThread(void* threadData)
{
    ThreadItem *pThread = static_cast<ThreadItem*>(threadData);
    CSocekt *pSocketObj = pThread->_pThis;
    time_t absolute_time,cur_time;
    int err;

    while(g_stopEvent == 0) //不退出
    {
        //这里没互斥判断，所以只是个初级判断，目的至少是队列为空时避免系统损耗
        if(pSocketObj->m_cur_size_ > 0)//队列不为空，有内容
        {
            //时间队列中最近发生事情的时间放到 absolute_time里；
            absolute_time = pSocketObj->m_timer_value_;
            cur_time = time(NULL);
            if(absolute_time < cur_time)
            {
                //时间到了，可以处理了
                std::list<LPSTRUC_MSG_HEADER> m_lsIdleList;
                LPSTRUC_MSG_HEADER result;

                if((err = pthread_mutex_lock(&pSocketObj->m_timequeueMutex)) != 0)
                {
                        ngx_log_stderr(err,"CSocekt::ServerTimerQueueMonitorThread()\
                            中pthread_mutex_lock()失败，返回的错误码为%d!",err);
                }
                
                while((result = pSocketObj->GetOverTimeTimer(cur_time)) != NULL)
                {
                    m_lsIdleList.push_back(result); 
                }

                if((err = pthread_mutex_unlock(&pSocketObj->m_timequeueMutex)) !=0)
                {
                    ngx_log_stderr(err,"CSocekt::ServerTimerQueueMonitorThread\
                        ()pthread_mutex_unlock()失败，返回的错误码为%d!",err);
                }

                LPSTRUC_MSG_HEADER tmpmsg;
                while(!m_lsIdleList.empty())
                {
                    tmpmsg = m_lsIdleList.front();
                    m_lsIdleList.pop_front();
                    /*这里需要检查心跳超时问题*/
                    pSocketObj->procPingTimeOutChecking(tmpmsg,cur_time);
                }
            }
        }
        usleep(500 * 1000);//为简化问题，我们直接每次休息500毫秒
    }
    return (void*)0;
}
/***************************************************************
 * @file       ngx_c_socket_time.cxx
 * @brief      心跳包检测时间到，该去检测心跳包是否超时的事宜，
 *             本函数只是把内存释放，子类应该重新事先该函数以实现具体的判断动作
 * @author     txj
 * @version    v1
 * @return     
 * @date       2021/7/29
 **************************************************************/
void CSocekt::procPingTimeOutChecking(LPSTRUC_MSG_HEADER tmpmsg,time_t cur_time)
{
    CMemory *p_memory = CMemory::GetInstance();
    p_memory->FreeMemory(tmpmsg);
}

